//
// Created by cy on 22-5-4.
//
#include "armor.hpp"
#include "except.hpp"

using namespace cv;
using namespace std;
using namespace spdlog;

class ArmorFinderPrivate {

public:
    explicit ArmorFinderPrivate(bool debug = false);

    FrameArray m_frames;
    BarInfoVector m_sutFindBarInfo;
    ArmorInfoVector m_sutDetectSmallArmorInfo;
    ArmorInfoVector m_sutDetectBigArmorInfo;
    // Debug模式下的图像数据
    ArmorDataVector m_sutArmors;
    // 装甲板数据集合
    sutArmorConfigs m_sutConfigs;
    // 装甲板识别参数

    bool m_debug;
    shared_ptr<spdlog::logger> m_outLogger;
    shared_ptr<spdlog::logger> m_errLogger;

    BarDataVector findLightBars(int ticks, const Mat &frame);

    [[nodiscard]] tuple<BarsTupleVector, BarsTupleVector> detectLightBars(const vector<sutBarData> &bars);

    ArmorDataVector createArmors(const tuple<BarsTupleVector, BarsTupleVector> &allBars, const Mat &frame);

    static array<Point2f, 2> getCorners(double borderX, double borderY, double length, double angle);

    static Point2f getCenter(const array<Point2f, 2> &leftCorner, const array<Point2f, 2> &rightCorner);

    bool checkBars(const sutBarData &leftBar, const sutBarData &rightBar, const sutDetectArmor &configs,
                   enuArmorType armorType);
};

ArmorFinderPrivate::ArmorFinderPrivate(bool debug) :
        m_debug(debug) {
    m_outLogger = stdout_color_st("ArmorOut");
    m_outLogger->set_level(level::info);
    m_outLogger->flush_on(level::info);

    m_errLogger = stderr_color_st("ArmorErr");
    m_errLogger->set_level(level::err);
    m_errLogger->flush_on(level::err);
}

BarDataVector ArmorFinderPrivate::findLightBars(int ticks, const Mat &frame) {
    sutFindBar configs = m_sutConfigs.findBar;
    Mat splitFrames[3];
    Mat frameGray;
    Mat frameBinary;
    vector<vector<cv::Point>> frameContours;
    BarDataVector barsBox;

    cv::split(frame, splitFrames);
    if (configs.enemyColor == enuArmorColor::COLOR_BLUE) {
        cv::subtract(splitFrames[0], splitFrames[2], frameGray);
//        this->m_outLogger->info("Blue!");
    } else {
        cv::subtract(splitFrames[2], splitFrames[0], frameGray);
    }
    // 对相减掉的白色区域进行补回
    cv::threshold(frameGray, frameBinary, configs.thresh, configs.maxVal, cv::THRESH_BINARY);
    auto &&kernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, configs.kernel);
    cv::morphologyEx(frameBinary, frameBinary, cv::MORPH_CLOSE, kernel);
    cv::findContours(frameBinary, frameContours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);

    if (m_debug) {
        m_frames[0] = frameGray;
        m_frames[1] = frameBinary;
        m_frames[2] = frame.clone();
        m_frames[3] = frame.clone();
    }

    int nums = 1;
    for (const auto &contour: frameContours) {
        auto area = cv::contourArea(contour);
        auto size = contour.size();
        if (area >= configs.areaSize && (double) size >= configs.contourSize) {
            auto &&rect = cv::fitEllipse(contour);
            auto angle = rect.angle > 90 ? rect.angle - 90 : rect.angle + 90;
            if (angle >= configs.ellipseAngleMin && angle <= configs.ellipseAngleMax) {
                barsBox.push_back({rect.center, rect.size.height, angle, ticks, enuArmorType::TYPE_NONE});
                // 将灯条数据放入容器里
                if (m_debug) {
                    m_sutFindBarInfo.push_back({area, int(size), angle});
                    cv::ellipse(m_frames[2], rect, Scalar(0, 0, 255), 1);
                    cv::putText(m_frames[2], fmt::format("{}", nums),
                                Point2i(rect.center), cv::FONT_HERSHEY_TRIPLEX,
                                0.8, Scalar(255, 255, 0));
                }
                // Debug模式下绘制灯条识别的图像
                nums += 1;

            }
        }
    }
    std::sort(barsBox.begin(), barsBox.end());
    // 此处对结构体进行了 < 重载，因此可以实现从左到右排序

    return barsBox;
}

tuple<BarsTupleVector, BarsTupleVector> ArmorFinderPrivate::detectLightBars(const vector<sutBarData> &bars) {
    sutDetectArmor smallConfig = this->m_sutConfigs.small;
    sutDetectArmor bigConfig = this->m_sutConfigs.big;
    BarsTupleVector smallBars, bigBars;

    auto barsNums = bars.size();
    if (barsNums != 0) {
        for (int i = 0; i < (barsNums - 1); i++) {
            for (int j = 1; j < (barsNums - 1 - i); j++) {
                if (ArmorFinderPrivate::checkBars(bars[i], bars[j], smallConfig, enuArmorType::TYPE_SMALL)) {
                    smallBars.push_back(make_tuple(bars[i], bars[j]));
                    break;
                }
                if (ArmorFinderPrivate::checkBars(bars[i], bars[j], bigConfig, enuArmorType::TYPE_BIG)) {
                    bigBars.push_back(make_tuple(bars[i], bars[j]));
                    break;
                }
            }
        }
    }
//    this->m_outLogger->info("准备返回所有灯条数据");
    return make_tuple(smallBars, bigBars);
}

vector<sutArmorData>
ArmorFinderPrivate::createArmors(const tuple<BarsTupleVector, BarsTupleVector> &allBars, const Mat &frame) {
    BarsTupleVector smallBars, bigBars;
    vector<sutArmorData> armors;
    tie(smallBars, bigBars) = allBars;


    for (auto barsTuple: smallBars) {
        sutBarData leftBar, rightBar;
        tie(leftBar, rightBar) = barsTuple;
        auto armorWidth = rightBar.center.x - leftBar.center.x;
        auto armorHeight = (rightBar.longLen + leftBar.longLen) / 2;
        auto armorAngle = (rightBar.angle + leftBar.angle) / 2;
        auto &&leftCorner = getCorners(leftBar.center.x, leftBar.center.y, armorHeight, armorAngle);
        auto &&rightCorner = getCorners(rightBar.center.x, rightBar.center.y, armorHeight, armorAngle);
        auto &&armorCenter = getCenter(leftCorner, rightCorner);
        armors.push_back({
                                 armorCenter, armorWidth, armorHeight,
                                 {leftCorner[0], rightCorner[0], rightCorner[1], leftCorner[1]}, leftBar.ticks,
                                 leftBar.type
                         });
        // 将构建好的小装甲板数据放入容器中
        if (m_debug) {
            cv::line(m_frames[3], Point2i(leftCorner[0]), Point2i(rightCorner[0]), Scalar(255, 213, 0), 2);
            cv::line(m_frames[3], Point2i(rightCorner[0]), Point2i(rightCorner[1]), Scalar(255, 213, 0), 2);
            cv::line(m_frames[3], Point2i(rightCorner[1]), Point2i(leftCorner[1]), Scalar(255, 213, 0), 2);
            cv::line(m_frames[3], Point2i(leftCorner[1]), Point2i(leftCorner[0]), Scalar(255, 213, 0), 2);
        }
    }

    for (auto barsTuple: bigBars) {
        sutBarData leftBar, rightBar;
        tie(leftBar, rightBar) = barsTuple;
        auto armorWidth = rightBar.center.x - leftBar.center.x;
        auto armorHeight = (rightBar.longLen + leftBar.longLen) / 2;
        auto armorAngle = (rightBar.angle + leftBar.angle) / 2;
        auto &&leftCorner = getCorners(leftBar.center.x, leftBar.center.y, armorHeight, armorAngle);
        auto &&rightCorner = getCorners(rightBar.center.x, rightBar.center.y, armorHeight, armorAngle);
        auto &&armorCenter = getCenter(leftCorner, rightCorner);
        armors.push_back({
                                 armorCenter, armorWidth, armorHeight,
                                 {leftCorner[0], rightCorner[0], rightCorner[1], leftCorner[1]}, leftBar.ticks,
                                 leftBar.type
                         });
        if (m_debug) {
            cv::line(m_frames[3], Point2i(leftCorner[0]), Point2i(rightCorner[0]), Scalar(255, 77, 0), 2);
            cv::line(m_frames[3], Point2i(rightCorner[0]), Point2i(rightCorner[1]), Scalar(255, 77, 0), 2);
            cv::line(m_frames[3], Point2i(rightCorner[1]), Point2i(leftCorner[1]), Scalar(255, 77, 0), 2);
            cv::line(m_frames[3], Point2i(leftCorner[1]), Point2i(leftCorner[0]), Scalar(255, 77, 0), 2);
        }
    }

    return armors;
}

array<Point2f, 2> ArmorFinderPrivate::getCorners(double borderX, double borderY, double length, double angle) {
    array<Point2f, 2> arrayPoints;
    if (angle >= 90) {
        angle = CV_PI / 180 * (180 - angle);
        Point2f pointDown(
                (float) (borderX - cos(angle) * (length / 2)),
                (float) (borderY + sin(angle) * (length / 2))
        );
        Point2f pointUp(
                (float) (borderX + cos(angle) * (length / 2)),
                (float) (borderY - sin(angle) * (length / 2))
        );
        arrayPoints = {pointUp, pointDown};
    } else {
        angle = CV_PI / 180 * angle;
        Point2f pointDown(
                (float) (borderX + cos(angle) * (length / 2)),
                (float) (borderY + sin(angle) * (length / 2))
        );
        Point2f pointUp(
                (float) (borderX - cos(angle) * (length / 2)),
                (float) (borderY - sin(angle) * (length / 2))
        );
        arrayPoints = {pointUp, pointDown};
    }
    return arrayPoints;
}

Point2f ArmorFinderPrivate::getCenter(const array<Point2f, 2> &leftCorner, const array<Point2f, 2> &rightCorner) {
    auto &leftUp = leftCorner[0], &leftDown = leftCorner[1];
    auto &rightUp = rightCorner[0], &rightDown = rightCorner[1];

    auto a1 = rightUp.y - leftDown.y;
    auto b1 = rightUp.x - leftDown.x;
    auto c1 = leftDown.x * rightUp.y - rightUp.x * leftDown.y;
    auto a2 = rightDown.y - leftUp.y;
    auto b2 = rightDown.x - leftUp.x;
    auto c2 = leftUp.x * rightDown.y - rightDown.x * leftUp.y;
    auto d = a1 * b2 - a2 * b1;
    d = d == 0 ? 1 : d;

    return {(b2 * c1 - b1 * c2) / d, (c1 * a2 - c2 * a1) / d};
}

bool ArmorFinderPrivate::checkBars(const sutBarData &leftBar, const sutBarData &rightBar, const sutDetectArmor &configs,
                                   enuArmorType armorType) {
    auto debug = this->m_debug;
    auto angleDiff = (double) abs(leftBar.angle - rightBar.angle);
    auto deltaX = rightBar.center.x - leftBar.center.x + 0.00001;
    auto deltaY = rightBar.center.y - leftBar.center.y;
    auto deviationAngle = abs(atan(deltaY / deltaX)) * 180 / CV_PI;
    auto xDiffRatio = abs(deltaX) / ((leftBar.longLen + rightBar.longLen) / 2);
    auto yDiffRatio = (double) abs(deltaY) / ((leftBar.longLen + rightBar.longLen) / 2);
    auto lengthDiffRatio = (double) abs(leftBar.longLen - rightBar.longLen) /
                           (leftBar.longLen > rightBar.longLen ? leftBar.longLen : rightBar.longLen);
    auto angleDiff_T = (angleDiff > configs.minAngleDiff && angleDiff < configs.maxAngleDiff);
    auto deviationAngle_T = (deviationAngle > configs.minDevAngle && deviationAngle < configs.maxDevAngle);
    auto xDiffRatio_T = (xDiffRatio > configs.minXDiffRatio && xDiffRatio < configs.maxXDiffRatio);
    auto yDiffRatio_T = (yDiffRatio > configs.minYDiffRatio && yDiffRatio < configs.maxYDiffRatio);
    auto lengthDiffRatio_T = (lengthDiffRatio > configs.minLDiffRatio && lengthDiffRatio < configs.maxLDiffRatio);

    auto isOk = angleDiff_T && deviationAngle_T && xDiffRatio_T && yDiffRatio_T && lengthDiffRatio_T;

    if (debug && isOk) {
        if (armorType == enuArmorType::TYPE_SMALL) {
            this->m_sutDetectSmallArmorInfo.push_back(
                    {angleDiff, deviationAngle, xDiffRatio, yDiffRatio, lengthDiffRatio});
        } else {
            this->m_sutDetectBigArmorInfo.push_back(
                    {angleDiff, deviationAngle, xDiffRatio, yDiffRatio, lengthDiffRatio});
        }
    }

    return isOk;
}


ArmorFinder::ArmorFinder(const string &configFile, bool debug) :
        m_ptr(new ArmorFinderPrivate(debug)) {
    fstream fs;
    fs.open(configFile, ios::in);
    if (!fs.is_open()) {
        m_ptr->m_errLogger->error("配置文件不存在，Armor模块的所有操作均无效!");
        throw CecException("配置文件不存在，Armor模块的所有操作均无效!");
    } else {
        m_ptr->m_outLogger->info("配置文件已找到，加载配置文件中...");
        Json::CharReaderBuilder reader;
        Json::Value j;
        JSONCPP_STRING err;

        bool res = Json::parseFromStream(reader, fs, &j, &err);
        if (!res || !err.empty()) {
            m_ptr->m_errLogger->error("加载配置文件时出现问题，错误原因->[{}]", err);
            throw CecException(fmt::format("加载配置文件时出现问题，错误原因->[{}]", err));
        } else {
            m_ptr->m_outLogger->info("配置文件加载成功!");
            try {
                if (j["FIND_BAR"]["enemyColor"].asString() == "R") {
                    m_ptr->m_sutConfigs.findBar.enemyColor = enuArmorColor::COLOR_RED;
                    m_ptr->m_outLogger->info("敌方颜色为: [红色]");
                } else {
                    m_ptr->m_sutConfigs.findBar.enemyColor = enuArmorColor::COLOR_BLUE;
                    m_ptr->m_outLogger->info("敌方颜色为: [蓝色]");
                }
                m_ptr->m_sutConfigs.findBar.thresh = j["FIND_BAR"]["thresh"].asInt();
                m_ptr->m_sutConfigs.findBar.maxVal = j["FIND_BAR"]["maxVal"].asInt();
                m_ptr->m_sutConfigs.findBar.kernel = Size2i(j["FIND_BAR"]["kernel"][0].asInt(),
                                                            j["FIND_BAR"]["kernel"][1].asInt());
                m_ptr->m_sutConfigs.findBar.areaSize = j["FIND_BAR"]["areaSize"].asDouble();
                m_ptr->m_sutConfigs.findBar.contourSize = j["FIND_BAR"]["contourSize"].asDouble();
                m_ptr->m_sutConfigs.findBar.ellipseAngleMin = j["FIND_BAR"]["ellipseAngleMin"].asDouble();
                m_ptr->m_sutConfigs.findBar.ellipseAngleMax = j["FIND_BAR"]["ellipseAngleMax"].asDouble();

                m_ptr->m_sutConfigs.small.minAngleDiff = j["SMALL"]["minAngleDiff"].asDouble();
                m_ptr->m_sutConfigs.small.maxAngleDiff = j["SMALL"]["maxAngleDiff"].asDouble();
                m_ptr->m_sutConfigs.small.minDevAngle = j["SMALL"]["minDevAngle"].asDouble();
                m_ptr->m_sutConfigs.small.maxDevAngle = j["SMALL"]["maxDevAngle"].asDouble();
                m_ptr->m_sutConfigs.small.minXDiffRatio = j["SMALL"]["minXDiffRatio"].asDouble();
                m_ptr->m_sutConfigs.small.maxXDiffRatio = j["SMALL"]["maxXDiffRatio"].asDouble();
                m_ptr->m_sutConfigs.small.minYDiffRatio = j["SMALL"]["minYDiffRatio"].asDouble();
                m_ptr->m_sutConfigs.small.maxYDiffRatio = j["SMALL"]["maxYDiffRatio"].asDouble();
                m_ptr->m_sutConfigs.small.minLDiffRatio = j["SMALL"]["minLDiffRatio"].asDouble();
                m_ptr->m_sutConfigs.small.maxLDiffRatio = j["SMALL"]["maxLDiffRatio"].asDouble();

                m_ptr->m_sutConfigs.big.minAngleDiff = j["BIG"]["minAngleDiff"].asDouble();
                m_ptr->m_sutConfigs.big.maxAngleDiff = j["BIG"]["maxAngleDiff"].asDouble();
                m_ptr->m_sutConfigs.big.minDevAngle = j["BIG"]["minDevAngle"].asDouble();
                m_ptr->m_sutConfigs.big.maxDevAngle = j["BIG"]["maxDevAngle"].asDouble();
                m_ptr->m_sutConfigs.big.minXDiffRatio = j["BIG"]["minXDiffRatio"].asDouble();
                m_ptr->m_sutConfigs.big.maxXDiffRatio = j["BIG"]["maxXDiffRatio"].asDouble();
                m_ptr->m_sutConfigs.big.minYDiffRatio = j["BIG"]["minYDiffRatio"].asDouble();
                m_ptr->m_sutConfigs.big.maxYDiffRatio = j["BIG"]["maxYDiffRatio"].asDouble();
                m_ptr->m_sutConfigs.big.minLDiffRatio = j["BIG"]["minLDiffRatio"].asDouble();
                m_ptr->m_sutConfigs.big.maxLDiffRatio = j["BIG"]["maxLDiffRatio"].asDouble();

                m_ptr->m_outLogger->info("参数载入成功!");
            } catch (Json::Exception &e) {
                m_ptr->m_errLogger->error("参数载入失败，请检查配置文件数据是否合理!");
//                m_ptr->m_errLogger->error("异常：[Json::exception] -> {}", e.what());
                throw CecException(e);
            }
        }
    }
    fs.close();
}

ArmorFinder::~ArmorFinder() {
    delete m_ptr;
}

bool ArmorFinder::update(int times, const Mat &frame, enuArmorColor team) {
//    auto armorsIsOk = !m_ptr->m_sutArmors.empty();
    if (!m_ptr->m_sutArmors.empty())
        m_ptr->m_sutArmors.clear();
    if (m_ptr->m_debug) {
        if (!m_ptr->m_sutFindBarInfo.empty())
            m_ptr->m_sutFindBarInfo.clear();
        if (!m_ptr->m_sutDetectSmallArmorInfo.empty())
            m_ptr->m_sutDetectSmallArmorInfo.clear();
        if (!m_ptr->m_sutDetectBigArmorInfo.empty())
            m_ptr->m_sutDetectBigArmorInfo.clear();
    }

    if (team != enuArmorColor::COLOR_NONE)
        m_ptr->m_sutConfigs.findBar.enemyColor = team;

    auto barsSpace = m_ptr->findLightBars(times, frame);
    if (barsSpace.empty()) {
        return false;
    }
    // 寻找到所有的灯条
    auto barsTuples = m_ptr->detectLightBars(barsSpace);
    BarsTupleVector bars1, bars2;
    tie(bars1, bars2) = barsTuples;
    if (bars1.empty() && bars2.empty()) {
        return false;
    }
    // 将匹配好的灯条放在一起
    auto armorsSpace = m_ptr->createArmors(barsTuples, frame);
    if (armorsSpace.empty()) {
        return false;
    } else {
        m_ptr->m_sutArmors = armorsSpace;
        return true;
    }
    // 将构建到的所有装甲板数据放在私有变量中
}

ArmorDataVector &ArmorFinder::result() {
    return m_ptr->m_sutArmors;
}

FrameArray &ArmorFinder::debugFramesInfo() {
    if (m_ptr->m_debug)
        return m_ptr->m_frames;
    else
        throw CecException("Debug模式未开启，无法获得图像调试数据!");
}

BarInfoVector &ArmorFinder::debugBarsInfo() {
    if (m_ptr->m_debug)
        return m_ptr->m_sutFindBarInfo;
    else
        throw CecException("Debug模式未开启，无法获得灯条调试数据!");
}

ArmorInfoVector &ArmorFinder::debugSmallArmorsInfo() {
    if (m_ptr->m_debug)
        return m_ptr->m_sutDetectSmallArmorInfo;
    else
        throw CecException("Debug模式未开启，无法获得装甲板调试数据!");
}

ArmorInfoVector &ArmorFinder::debugBigArmorsInfo() {
    if (m_ptr->m_debug)
        return m_ptr->m_sutDetectBigArmorInfo;
    else
        throw CecException("Debug模式未开启，无法获得装甲板调试数据!");
}

void ArmorFinder::updateConfigs(const sutArmorConfigs &configs) {
    m_ptr->m_sutConfigs = configs;
}

void ArmorFinder::saveConfigs(const string &configFile) {
    fstream fs;
    fs.open(configFile, ios::out | ios::trunc);
    if (!fs.is_open()) {
        m_ptr->m_errLogger->error("配置文件不存在，无法保存配置信息...");
        throw CecException("配置文件不存在，无法保存配置信息...");
    } else {
        m_ptr->m_outLogger->info("配置文件已找到，正在保存配置信息...");
        Json::StyledStreamWriter writer;
        Json::Value j;

        if (m_ptr->m_sutConfigs.findBar.enemyColor == enuArmorColor::COLOR_RED) {
            j["FIND_BAR"]["enemyColor"] = "R";
        } else {
            j["FIND_BAR"]["enemyColor"] = "B";
        }
        try {
            j["FIND_BAR"]["thresh"] = m_ptr->m_sutConfigs.findBar.thresh;
            j["FIND_BAR"]["maxVal"] = m_ptr->m_sutConfigs.findBar.maxVal;
            j["FIND_BAR"]["kernel"][0] = m_ptr->m_sutConfigs.findBar.kernel.width;
            j["FIND_BAR"]["kernel"][1] = m_ptr->m_sutConfigs.findBar.kernel.height;
            j["FIND_BAR"]["areaSize"] = m_ptr->m_sutConfigs.findBar.areaSize;
            j["FIND_BAR"]["contourSize"] = m_ptr->m_sutConfigs.findBar.contourSize;
            j["FIND_BAR"]["ellipseAngleMin"] = m_ptr->m_sutConfigs.findBar.ellipseAngleMin;
            j["FIND_BAR"]["ellipseAngleMax"] = m_ptr->m_sutConfigs.findBar.ellipseAngleMax;

            j["SMALL"]["minAngleDiff"] = m_ptr->m_sutConfigs.small.minAngleDiff;
            j["SMALL"]["maxAngleDiff"] = m_ptr->m_sutConfigs.small.maxAngleDiff;
            j["SMALL"]["minDevAngle"] = m_ptr->m_sutConfigs.small.minDevAngle;
            j["SMALL"]["maxDevAngle"] = m_ptr->m_sutConfigs.small.maxDevAngle;
            j["SMALL"]["minXDiffRatio"] = m_ptr->m_sutConfigs.small.minXDiffRatio;
            j["SMALL"]["maxXDiffRatio"] = m_ptr->m_sutConfigs.small.maxXDiffRatio;
            j["SMALL"]["minYDiffRatio"] = m_ptr->m_sutConfigs.small.minYDiffRatio;
            j["SMALL"]["maxYDiffRatio"] = m_ptr->m_sutConfigs.small.maxYDiffRatio;
            j["SMALL"]["minLDiffRatio"] = m_ptr->m_sutConfigs.small.minLDiffRatio;
            j["SMALL"]["maxLDiffRatio"] = m_ptr->m_sutConfigs.small.maxLDiffRatio;

            j["BIG"]["minAngleDiff"] = m_ptr->m_sutConfigs.big.minAngleDiff;
            j["BIG"]["maxAngleDiff"] = m_ptr->m_sutConfigs.big.maxAngleDiff;
            j["BIG"]["minDevAngle"] = m_ptr->m_sutConfigs.big.minDevAngle;
            j["BIG"]["maxDevAngle"] = m_ptr->m_sutConfigs.big.maxDevAngle;
            j["BIG"]["minXDiffRatio"] = m_ptr->m_sutConfigs.big.minXDiffRatio;
            j["BIG"]["maxXDiffRatio"] = m_ptr->m_sutConfigs.big.maxXDiffRatio;
            j["BIG"]["minYDiffRatio"] = m_ptr->m_sutConfigs.big.minYDiffRatio;
            j["BIG"]["maxYDiffRatio"] = m_ptr->m_sutConfigs.big.maxYDiffRatio;
            j["BIG"]["minLDiffRatio"] = m_ptr->m_sutConfigs.big.minLDiffRatio;
            j["BIG"]["maxLDiffRatio"] = m_ptr->m_sutConfigs.big.maxLDiffRatio;
        } catch (Json::Exception &e) {
            m_ptr->m_errLogger->error("配置文件保存失败，请检查配置文件数据是否合理!");
            throw CecException(e);
        }
        writer.write(fs, j);
//        fs << Json::writeString(writer,j);
    }
    fs.close();
}

sutArmorConfigs &ArmorFinder::getConfigs() {
    return m_ptr->m_sutConfigs;
}

void ArmorFinder::openDebug() {
    m_ptr->m_debug = true;
}

void ArmorFinder::closeDebug() {
    m_ptr->m_debug = false;
}